﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using Xceed.Silverlight.ListBox.PositioningFunction;
using Xceed.Silverlight.ListBox.Utils;
using Xceed.Silverlight.Data;
using Xceed.Silverlight.Utils;
using System.Collections.ObjectModel;

namespace Xceed.Silverlight.ListBox
{
  public sealed class PathPanel : PanelBase
  {
    #region PathConfiguration Property

    public static readonly DependencyProperty PathConfigurationProperty = DependencyProperty.Register(
      "PathConfiguration",
      typeof( PathConfiguration ),
      typeof( PathPanel ),
      new PropertyMetadata( null, new PropertyChangedCallback( PathPanel.OnPathConfigurationChanged ) ) );

    public PathConfiguration PathConfiguration
    {
      get
      {
        return ( PathConfiguration )this.GetValue( PathPanel.PathConfigurationProperty );
      }
      set
      {
        this.SetValue( PathPanel.PathConfigurationProperty, value );
      }
    }

    private static void OnPathConfigurationChanged( object sender, DependencyPropertyChangedEventArgs e )
    {
      PathPanel pathView = sender as PathPanel;

      if( pathView.IsInitializing )
        return;

      PathConfiguration newPathConfiguration = e.NewValue as PathConfiguration;

      if( newPathConfiguration == null )
        throw new ArgumentNullException( "PathConfiguration cannot be null." );


      PathConfiguration oldPathConfiguration = e.OldValue as PathConfiguration;

      if( oldPathConfiguration != null )
      {
        oldPathConfiguration.PositioningFunctionChanged -= new EventHandler( pathView.PathConfigurationPositionFunctionChanged );
        oldPathConfiguration.ScalingFunctionChanged -= new EventHandler( pathView.PathConfigurationScalingFunctionChanged );
        oldPathConfiguration.ZIndexFunctionChanged -= new EventHandler( pathView.PathConfigurationZIndexFunctionChanged );
      }

      newPathConfiguration.PositioningFunctionChanged += new EventHandler( pathView.PathConfigurationPositionFunctionChanged );
      newPathConfiguration.ScalingFunctionChanged += new EventHandler( pathView.PathConfigurationScalingFunctionChanged );
      newPathConfiguration.ZIndexFunctionChanged += new EventHandler( pathView.PathConfigurationZIndexFunctionChanged );

      pathView.UpdateFunctionsFromPathConfiguration();
    }

    private void PathConfigurationPositionFunctionChanged( object sender, EventArgs e )
    {
      PathConfiguration pathConfiguration = ( PathConfiguration )sender;

      this.PositioningFunction = pathConfiguration.PositioningFunction;
    }

    private void PathConfigurationScalingFunctionChanged( object sender, EventArgs e )
    {
      PathConfiguration pathConfiguration = ( PathConfiguration )sender;

      this.ScalingFunction = pathConfiguration.ScalingFunction;
    }

    private void PathConfigurationZIndexFunctionChanged( object sender, EventArgs e )
    {
      PathConfiguration pathConfiguration = ( PathConfiguration )sender;

      this.ZIndexFunction = pathConfiguration.ZIndexFunction;
    }

    private void UpdateFunctionsFromPathConfiguration()
    {
      this.PositioningFunction = this.PathConfiguration.PositioningFunction;
      this.ScalingFunction = this.PathConfiguration.ScalingFunction;
      this.ZIndexFunction = this.PathConfiguration.ZIndexFunction;
    }

    #endregion

    #region ScalingFunction Private Property

    private IScalingFunction ScalingFunction
    {
      get;
      set;
    }

    #endregion

    #region ZIndexFunction Private Property

    private IZIndexFunction ZIndexFunction
    {
      get;
      set;
    }

    #endregion


    #region BringLastItemToStartingPoint

    internal override bool BringLastItemToStartingPoint
    {
      get
      {
        return ( this.PathConfiguration.StartOffset > 0.0d );
      }
    }

    #endregion


    #region AllowStickyHeaders Internal Read-Only Property

    internal override bool AllowStickyHeaders
    {
      get
      {
        return false;
      }
    } 

    #endregion

    internal override bool IsContainerOutOfViewportSupported
    {
      get
      {
        // Always consider every layouted containers as into view
        // for path panel.
        return false;
      }
    }

    internal override ContainerLayoutMode GetPositioningContainerLayoutMode()
    {
      return ContainerLayoutMode.Floating;
    }

    internal override IScalingFunction GetScalingFunction()
    {
      return this.ScalingFunction;
    }

    internal override IZIndexFunction GetZIndexFunction()
    {
      return this.ZIndexFunction;
    }

    internal override TransitionAnimation CreateScrollingUpTransitionAnimation()
    {
      return new LoadingTransitionAnimation( this.ParentListBox.AnimationSettings );
    }

    internal override TransitionAnimation CreateScrollingDownTransitionAnimation()
    {
      return new LoadingTransitionAnimation( this.ParentListBox.AnimationSettings );
    }

    internal override void AdjustContainerTargetPosition( ListBoxContainer container )
    {
      // This method adjust the position of the final containers (scaled/rotated)
      // to align the container's center on the path.

      Point absoluteOffset = container.AbsoluteOffset;
      Size cachedSize = container.Configuration.CachedSize;
      double scaleFactor = container.AbsoluteScaleFactor;

      container.AbsoluteOffset = new Point( absoluteOffset.X - ( ( cachedSize.Width / 2 ) * scaleFactor ),
            absoluteOffset.Y - ( ( cachedSize.Height / 2 ) * scaleFactor ) );

      base.AdjustContainerTargetPosition( container );
    }

    internal override void EndInitCore()
    {
      if( this.PathConfiguration == null )
        throw new ListBoxException( "PathConfiguration cannot be null on the PathView." );

      base.EndInitCore();

      this.PathConfiguration.PositioningFunctionChanged += new EventHandler( this.PathConfigurationPositionFunctionChanged );
      this.PathConfiguration.ScalingFunctionChanged += new EventHandler( this.PathConfigurationScalingFunctionChanged );
      this.PathConfiguration.ZIndexFunctionChanged += new EventHandler( this.PathConfigurationZIndexFunctionChanged );

      this.UpdateFunctionsFromPathConfiguration();
    }
  }
}
